package com.zengdada.authority.utils.where;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.zengdada.authority.utils.CommonUtils;
import com.zengdada.authority.utils.PublicSearchRangeAssist;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author zsa
 * @date 2020/9/9 14:59
 */
public class Where {
    /**
     * 字段
     */
    private String field;
    /**
     * 值params
     */
    private String value;
    /**
     * 关系
     */
    private String formula;
    /**
     * 关系 枚举
     */
    private Formula formulaE;
    /**
     * 子条件
     */
    private List<Where> params;
    /**
     * 数据类型 (兼容旧版本)
     */
    private String type = "string";
    /**
     * 数据类型
     */
    private String fieldType = "string";
    /**
     * 数据类型 枚举
     */
    private FieldType fieldTypeE = FieldType.STRING;

    /**
     * 时间类型
     */
    private String dateType = "date";
    /**
     * 时间类型 枚举
     */
    private DateType dateTypeE = DateType.DATE;
    private static final Pattern SQL_CHECK = Pattern.compile("[\u4e00-\u9fa5_A-Za-z0-9\\_]+$");

    public enum DateType {
        /**
         * 年
         */
        YEAR("year", "yyyy", "yyyy"),
        /**
         * 月
         */
        MONTH("month", "yyyy-mm", "yyyy-MM"),
        /**
         * 天
         */
        DATE("date", "yyyy-mm-dd", "yyyy-MM-dd"),
        /**
         * 时间
         */
        TIME("time", "hh24:mi:ss", "HH:mm:ss"),
        /**
         * 日期 + 时间
         */
        DATETIME("datetime", "yyyy-MM-dd hh24:mi:ss", "yyyy-MM-dd HH:mm:ss");
        private String type;
        private String dbPattern;
        private String pattern;

        DateType(String type, String dbPattern, String pattern) {
            this.type = type;
            this.dbPattern = dbPattern;
            this.pattern = pattern;
        }

        public String getType() {
            return type;
        }

        public String getDbPattern() {
            return dbPattern;
        }

        public String getPattern() {
            return pattern;
        }
    }

    public enum FieldType {
        /**
         * 字符串类型
         */
        STRING("STRING", String.class),
        /**
         * 数字类型
         */
        NUMBER("NUMBER", BigDecimal.class),
        /**
         * 整型
         */
        INTEGER("INT", Integer.class),
        /**
         * 长整型
         */
        LONG("LONG", Long.class),
        /**
         * 日期类型
         */
        DATE("DATE", Date.class);

        private Class<? extends Object> javaType;
        private String name;

        FieldType(String name, Class<? extends Object> javaType) {
            this.name = name;
            this.javaType = javaType;
        }

        public Class<? extends Object> getJavaType() {
            return javaType;
        }

        public void setJavaType(Class<? extends Object> javaType) {
            this.javaType = javaType;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public enum Formula {
        /**
         * AND
         */
        AND("AND"),
        /**
         * OR
         */
        OR("OR"),
        /**
         * 等于
         */
        EQUAL("="),
        /**
         * 为空
         */
        IS_NULL("NULL"),
        /**
         * 不为空
         */
        IS_NOT_NULL("NOTNULL"),
        /**
         * 不等于
         */
        NOT_EQUAL("<>"),
        /**
         * 大于
         */
        GREATER(">"),
        /**
         * 小于
         */
        LESS("<"),
        /**
         * in
         */
        IN("IN"),
        /**
         * not in
         */
        NOT_IN("NOTIN"),
        /**
         * 包含
         */
        LIKE("<->"),
        /**
         * 开始于
         */
        START("->"),
        /**
         * 结束于
         */
        END("<-"),
        /**
         * 结束于
         */
        RANGE("RANGE");
        private String symbol;

        Formula(String symbol) {
            this.symbol = symbol;
        }

        public String getSymbol() {
            return symbol;
        }
    }

    public Where() {
    }

    public Where(String field, String value) {
        setField(field);
        setValue(value);
    }

    public Where(String field, String formula, String value) {
        setField(field);
        setFormula(formula);
        setValue(value);
    }

    public Where(String field, String formula, String value, String type, String dateType) {
        setField(field);
        setFormula(formula);
        setValue(value);
        setFieldType(type);
        setDateType(dateType);
    }

    public Where(String field, Formula formula, String value) {
        setField(field);
        setFormulaE(formula);
        setValue(value);
    }

    public Where(String field, Formula formula, String value, FieldType fieldType) {
        setField(field);
        setFormulaE(formula);
        setValue(value);
        setFieldType(fieldType);
    }

    public Where(String field, String value, FieldType fieldType) {
        setField(field);
        setFieldType(fieldType);
        setValue(value);
    }

    public Where(String field, Formula formula, String value, FieldType fieldType, DateType dateType) {
        setField(field);
        setFormulaE(formula);
        setValue(value);
        setFieldType(fieldType);
        setDateTypeE(dateType);
    }

    public Where(String formula, List<Where> params) {
        setFormula(formula);
        this.params = params;
    }

    public Where(Formula formulaE, List<Where> params) {
        setFormulaE(formulaE);
        this.params = params;
    }

    public String getField() {
        return field;
    }

    public Where setField(String field) {
        if (!SQL_CHECK.matcher(field).matches()) {
            throw new RuntimeException(String.format("错误的查询字段‘%s’（仅允许中文、字母、数字及下划线）", field));
        }
        this.field = field;
        return this;
    }

    public String getValue() {
        return value;
    }

    public Where setValue(String value) {
        this.value = value;
        return this;
    }

    public String getFormula() {
        return formula;
    }

    public Where setFormula(String formula) {
        try {
            this.formulaE = CommonUtils.enumValueOf(Formula.class, new HashMap<String, Object>(1) {{
                put("symbol", formula.toUpperCase());
            }});
            if (this.formulaE != null) {
                this.formula = this.formulaE.getSymbol();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    public Formula getFormulaE() {
        return formulaE;
    }


    public Where setFormulaE(Formula formula) {
        if (formula != null) {
            this.formulaE = formula;
        }
        this.formula = this.formulaE.getSymbol();
        return this;
    }

    public List<Where> getParams() {
        return params;
    }

    public Where setParams(List<Where> params) {
        this.params = params;
        return this;
    }

    public String getType() {
        return type;
    }

    public Where setType(String fieldType) {
        setFieldType(fieldType);
        this.type = this.fieldType;
        return this;
    }

    public String getFieldType() {
        return fieldType;
    }

    public Where setFieldType(String fieldType) {
        try {
            this.fieldTypeE = CommonUtils.enumValueOf(FieldType.class, new HashMap<String, Object>(1) {{
                put("name", fieldType.toUpperCase());
            }});
        } catch (Exception e) {
            //
        }
        if (this.fieldTypeE == null) {
            this.fieldTypeE = FieldType.STRING;
        }
        this.fieldType = this.fieldTypeE.getName();
        return this;
    }

    public Where setFieldType(FieldType type) {
        if (type != null) {
            this.fieldTypeE = type;
            this.fieldType = type.getName();
        }
        return this;
    }

    public String getDateType() {
        return dateType;
    }

    public Where setDateType(String dateType) {
        try {
            this.dateTypeE = DateType.valueOf(dateType.toUpperCase());
        } catch (Exception e) {
            //
        }
        if (this.dateTypeE == null) {
            this.dateTypeE = DateType.DATE;
        }
        this.dateType = this.dateTypeE.getType();
        return this;
    }

    public Where setDateTypeE(DateType dateType) {
        if (dateType != null) {
            this.dateTypeE = dateType;
            this.dateType = this.dateTypeE.getType();
        }
        return this;
    }


    public Object getCriteriaConditionVal() {
        initFormula();
        Object result = null;
        switch (this.formulaE) {
            case OR:
            case AND:
            case IS_NULL:
            case IS_NOT_NULL:
                break;
            case LIKE:
//                result = "%" + this.value + "%";
                result = this.value;
                break;
            case END:
                result = "%" + this.value;
                break;
            case START:
                result = this.value + "%";
                break;
            case IN:
            case NOT_IN:
                try {
                    if (this.fieldTypeE == FieldType.DATE) {
                        JSONObject.DEFFAULT_DATE_FORMAT = this.dateTypeE.getPattern();
                        result = JSONObject.parseArray(this.value, this.fieldTypeE.getJavaType());
                    } else {
                        result = JSONObject.parseArray(this.value, this.fieldTypeE.getJavaType());
                    }
                } catch (Exception e) {
                    try {
                        if (this.fieldTypeE == FieldType.DATE) {
                            JSONObject.DEFFAULT_DATE_FORMAT = this.dateTypeE.getPattern();
                            ArrayList<String> list = new ArrayList<>(Arrays.asList(this.value.split(",")));
                            result = JSONObject.parseArray(JSONObject.toJSONString(list), this.fieldTypeE.getJavaType());
                        } else if (this.fieldTypeE == FieldType.STRING) {
                            result = Arrays.asList(this.value.split(","));
                        } else {
                            result = Arrays.stream(this.value.split(",")).map(i -> JSONObject.parseObject(i, this.fieldTypeE.getJavaType())).collect(Collectors.toList());
                        }
                    } catch (Exception e1) {
                        result = Arrays.asList(this.value.split(","));
                    }
                }
                break;
            case RANGE:
                try {
                    String[] params = this.value.split("~~");
                    if (this.fieldTypeE == FieldType.DATE) {
                        SimpleDateFormat sdf = new SimpleDateFormat(this.dateTypeE.getPattern(), Locale.CHINA);
                        result = new PublicSearchRangeAssist(sdf.parse(params[0]), sdf.parse(params[1]));
                    } else if (this.fieldTypeE == FieldType.STRING) {
                        result = new PublicSearchRangeAssist(params[0], params[1]);
                    } else {
                        result = new PublicSearchRangeAssist(JSONObject.parseObject(params[0], this.fieldTypeE.getJavaType()), JSONObject.parseObject(params[1], this.fieldTypeE.getJavaType()));
                    }
                } catch (Exception e) {
                    throw new RuntimeException("范围参数解析错误！！" + e.getMessage());
                }
                break;
            default:
                try {
                    switch (this.fieldTypeE) {
                        case DATE:
                            SimpleDateFormat sdf = new SimpleDateFormat(this.dateTypeE.getPattern(), Locale.CHINA);
                            result = sdf.parse(this.value);
                            break;
                        case STRING:
                            result = this.value;
                            break;
                        default:
                            result = JSONObject.parseObject(this.value, this.fieldTypeE.getJavaType());
                            break;
                    }
                } catch (Exception e) {
                    result = this.value;
                }
        }
        return result;
    }

    public Object getConditionVal() {
        return getCriteriaConditionVal();
    }

    /**
     * mapper 得到前缀
     *
     * @return
     */
    public String getConditionAndPrefix() {
        if (field == null) {
            return null;
        }
        return getConditionAndPrefix(CommonUtils.humpToLine(field));
    }


    public String getConditionAndPrefix(String field) {
        initFormula();
        String condition;
        switch (formulaE) {
            case GREATER:
            case LESS:
            case EQUAL:
            case NOT_EQUAL:
                condition = String.format("%s %s ", field, formulaE.getSymbol());
                break;
            case LIKE:
                condition = String.format("instr(%s, ", field);
                break;
            case END:
            case START:
                condition = String.format("%s LIKE ", field);
                break;
            case IN:
                condition = String.format("%s IN(", field);
                break;
            case NOT_IN:
                condition = String.format("%s NOT IN(", field);
                break;
            case IS_NULL:
                condition = String.format("%s IS NULL", field);
                break;
            case IS_NOT_NULL:
                condition = String.format("%s IS NOT NULL", field);
                break;
            case RANGE:
                condition = String.format("%s BETWEEN ", field);
                break;
            default:
                condition = "";
        }
        return condition;
    }


    /**
     * mapper 得到后缀
     *
     * @return
     */
    public String getConditionSuffix() {
        initFormula();
        String suffix = "";
        if (formulaE == Formula.IN || formulaE == Formula.NOT_IN) {
            suffix += ")";
        }
        // oracle 提高效率
        if (formulaE == Formula.LIKE) {
            suffix += ") > 0";
        }
        return suffix;
    }


    public boolean isValid() {
        if (this.formulaE == Formula.OR || this.formulaE == Formula.AND) {
            return !params.isEmpty();
        }
        if (StringUtils.isBlank(this.field)) {
            return false;
        }
        if (getConditionVal() == null && !(this.formulaE == Formula.IS_NULL || this.formulaE == Formula.IS_NOT_NULL)) {
            return false;
        }
        return true;
    }

    public boolean isRel() {
        if (this.formulaE == Formula.OR || this.formulaE == Formula.AND) {
            return true;
        }
        return false;
    }


    private void initFormula() {
        if (this.formulaE == null) {
            if (this.params == null || this.params.isEmpty()) {
                setFormulaE(Formula.EQUAL);
            } else {
                setFormulaE(Formula.AND);
            }
        }
    }

    public static Where formJsonString(String jsonStr) {
        Object parse = JSONObject.parse(jsonStr);
        if (parse instanceof JSONObject) {
            return ((JSONObject) parse).toJavaObject(Where.class);
        } else if (parse instanceof JSONArray) {
            List<Where> ws = ((JSONArray) parse).toJavaObject(new TypeReference<List<Where>>() {
            });
            return new Where(Formula.AND, ws);
        }
        return null;
    }
}
